<?php
/**
 * 创建人 : Forpastime
 * 创建时间: 2023/3/9 13:24
 * 个人主页 : http://forpastime.com
 */

namespace App\Service;


use App\Utils\Result;
use EasyWeChat\Factory;
use EasyWeChat\Kernel\Messages\Text;
use EasyWeChat\Kernel\Messages\Transfer;
use EasyWeChat\OfficialAccount\CustomerService\Messenger;
use EasyWeChat\Work\GroupRobot\Messages\Message;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Redis\Redis;
use Hyperf\Utils\ApplicationContext;

class WechatService
{
    protected $app;
    protected $config;
    protected $clientType;

    /**
     * WechatService constructor.
     * @param $clientType
     * @param array $config
     * @param string $appId
     */
    public function __construct($clientType='h5',$config=[])
    {
        switch ($clientType){
            //微信小程序
            case 'mp':
                if(empty($config)){
                    $config = [
                        'app_id' => env('wx_mp_appid',''),
                        'secret' => env('wx_mp_secret',''),
                        'mch_id' => env('wx_mch_id',''),
                        'key'    => env('wx_mch_key',''),   // API v2 密钥 (注意: 是v2密钥 是v2密钥 是v2密钥)
                        'cert_path'  => env('wx_cert_path',''), // XXX: 绝对路径！！！！
                        'key_path'   => env('wx_key_path',''),      // XXX: 绝对路径！！！！
                        'notify_url'         => env('wx_notify_url',''),
                        // 下面为可选项
                        // 指定 API 调用返回结果的类型：array(default)/collection/object/raw/自定义类名
                        'response_type' => 'array',

                        'log' => [
                            'level' => 'debug',
                            'file' => env('wx_log_file',''),
                        ],
                    ];
                }
                $this->app = Factory::miniProgram($config);
                break;
            case 'h5':
                if(empty($config)){
                    $config = [
                        'app_id' => env('wx_official_appid',''),
                        'secret' => env('wx_official_secret',''),
                        'mch_id' => env('wx_mch_id',''),
                        'key'    => env('wx_mch_key',''),   // API v2 密钥 (注意: 是v2密钥 是v2密钥 是v2密钥)
                        'cert_path'  => env('wx_cert_path',''), // XXX: 绝对路径！！！！
                        'key_path'   => env('wx_key_path',''),      // XXX: 绝对路径！！！！
                        'notify_url'         => env('wx_notify_url',''),
                        'token' => env('wx_official_token'),
                        'aes_key' => env('wx_official_aes_key'),
                        // 指定 API 调用返回结果的类型：array(default)/collection/object/raw/自定义类名
                        'response_type' => 'array',

                    ];
                }

                $this->app = Factory::officialAccount($config);
                break;

            //开放平台
            case 'pc':
                if(empty($config)){
                    $config = [
                        'app_id'   => env('wx_open_appid',''),
                        'secret'   => env('wx_open_secret',''),
                        'mch_id' => env('wx_mch_id',''),
                        'key'    => env('wx_mch_key',''),   // API v2 密钥 (注意: 是v2密钥 是v2密钥 是v2密钥)
                        'cert_path'  => env('wx_cert_path',''), // XXX: 绝对路径！！！！
                        'key_path'   => env('wx_key_path',''),      // XXX: 绝对路径！！！！
                        'notify_url' => env('wx_notify_url',''),
                        'token'    => env('wx_open_token',''),
                        'aes_key'  => env('wx_open_aes_key','')
                    ];
                }

                $this->app = Factory::openPlatform($config);
                break;
            case 'app':
                if(empty($config)){
                    $config = [
                        'app_id'   => env('wx_open_appid',''),
                        'secret'   => env('wx_open_secret',''),
                        'mch_id' => env('wx_mch_id',''),
                        'key'    => env('wx_mch_key',''),   // API v2 密钥 (注意: 是v2密钥 是v2密钥 是v2密钥)
                        'cert_path'  => env('wx_cert_path',''), // XXX: 绝对路径！！！！
                        'key_path'   => env('wx_key_path',''),      // XXX: 绝对路径！！！！
                        'notify_url' => env('wx_notify_url',''),
                        'token'    => env('wx_open_token',''),
                        'aes_key'  => env('wx_open_aes_key','')
                    ];
                }

                $this->app = Factory::openPlatform($config);
                break;
            case 'work':
                if(empty($config)){
                    $config = [
                        'corp_id' => env('wx_open_corp_id',''),

                        'agent_id' => env('wx_open_agent_id',''), // 如果有 agend_id 则填写
                        'secret'   =>  env('wx_open_secret',''),
                        'mch_id' => env('wx_mch_id',''),
                        'key'    => env('wx_mch_key',''),   // API v2 密钥 (注意: 是v2密钥 是v2密钥 是v2密钥)
                        'cert_path'  => env('wx_cert_path',''), // XXX: 绝对路径！！！！
                        'key_path'   => env('wx_key_path',''),      // XXX: 绝对路径！！！！
                        'notify_url' => env('wx_notify_url',''),
                        // 指定 API 调用返回结果的类型：array(default)/collection/object/raw/自定义类名
                        'response_type' => 'array',

                        'log' => [
                            'level' => 'debug',
                            'file' => __DIR__.'/wechat.log',
                        ],
                    ];
                }

                $this->app = Factory::work($config);
                break;
            default:

                break;
        }
        $this->config = $config;
        $this->clientType = $clientType;
    }

    /**
     * 返回微信分享用的配置参数 如果不想用默认读取的URL，可以使用此方法手动设置，通常不需要。
     * @param string $url
     * @return mixed
     */
    public function officialConfig($url='',$debug=false)
    {
        if($url!=''){
            $this->app->jssdk->setUrl($url);
        }
        $res = $this->app->jssdk->buildConfig(['updateAppMessageShareData','updateTimelineShareData'],$debug, false, true,  []);

        return $res;
    }

    /**
     * 生成二维码 ，一般用于公众号扫码
     */
    public function getQrcode($scene="bind",$expire=6*24*3600)
    {
        return $this->app->qrcode->temporary($scene,$expire);
    }

    /**
     * 公众号服务器Token验证
     */
    public function checkToken()
    {
        $response = $this->app->server->serve();
        $response->send();
        echo 'checkToken-getStatusCode：'.$response->getStatusCode().PHP_EOL;
        return $response->getStatusCode();
    }

    /**
     * 服务返回自定义消息
     * @param $message
     * @param $tips
     */
    public function sendSuccessMsg($message,$tips)
    {
        $app = $this->app;
        $this->app->server->push(function ($msg) use ($message,$tips,$app){
            $app->customer_service->message($message)->to($message['FromUserName'])->send();
        });
        $response = $this->app->server->serve();
        $response->send();
        return $response;
    }

    /**
     * 处理公众号事件
     */
    public function event($message)
    {
        //echo("openid:".$message['openid']);
        $app = $this->app;

        $this->app->server->push(function ($msg) use ($message,$app){

//            var_dump('push',$message);

            switch ($message['MsgType']) {
                case 'event':
                    switch ($message['Event']) {
                        case 'subscribe':
                            $userinfo = $this->app->user->get($message['openid']);
                            //var_dump('EventKey:',$message['EventKey'],$userinfo);

                            // 判断订阅是否源于登录扫码
                            if (!is_array($message['EventKey']) && strpos($message['EventKey'],'qrscene_login') !== false) {
                                $redis = ApplicationContext::getContainer()->get(Redis::class);
                                //$redis->hMSet(,$userinfo);
                                $redis->setex($message['EventKey'],3600,json_encode($userinfo));
                                $app->customer_service->message('登陆成功')->to($message['FromUserName'])->send();
                                //return '登陆成功';
                            }
                            break;
                        case 'SCAN':
                            //判断是登陆，查询有用户信息保存到缓存供验证接口调用
                            if (!is_array($message['EventKey']) && strpos($message['EventKey'],'login_') !== false) {
                                $redis = ApplicationContext::getContainer()->get(Redis::class);
                                $userinfo = $this->app->user->get($message['openid']);
                                //var_dump('EventKey:SCAN==->app->user:',$userinfo);
                                $redis->setex("qrscene_".$message['EventKey'],3600,json_encode($userinfo));
                                $app->customer_service->message('登陆成功')->to($message['FromUserName'])->send();
                            }
                            break;
                    }
                    return '收到事件消息';
                    break;
                case 'text':
                    $app->customer_service->message($message)->to($message['FromUserName'])->send();
                    break;
                case 'image':

                    return '收到图片消息';
                    break;
                case 'voice':
                    return '收到语音消息';
                    break;
                case 'video':
                    return '收到视频消息';
                    break;
                case 'location':
                    return '收到坐标消息';
                    break;
                case 'link':
                    return '收到链接消息';
                    break;
                case 'file':
                    return '收到文件消息';
                // ... 其它消息
                default:
                    return '收到其它消息';
                    break;
            }
        });
        var_dump($this->app->server);
        $response = $this->app->server->serve();
        $response->send();
        return $response;
    }

    public function transfer($message,$account)
    {
        $app = $this->app;
        $this->app->server->push(function ($msg) use ($message,$account,$app){
            $app->customer_service->message($message)->from($message['FromUserName'])->to($message['ToUserName'])->send();
            return new Transfer();
        });
        $response = $this->app->server->serve();
        $response->send();
        return $response;
    }
    /**
     * 创建菜单
     * @param array $menu
     */
    public function createMenu($menu = [])
    {
        if(!empty($menu)){
            return $this->app->menu->create($menu);
        }
    }

    /**
     * 获取支持的行业列表
     */
    public function getIndustry()
    {
        return $this->app->template_message->getIndustry();
    }
    /**
     * 添加模板 在公众号后台获取 $shortId 并添加到账户。
     */
    public function addTemplate($shortId)
    {
        return $this->app->template_message->addTemplate($shortId);
    }
    /**
     * 获取所有模板列表
     */
    public function getPrivateTemplates()
    {
        return $this->app->template_message->getPrivateTemplates();
    }
    /**
     * 发送模板消息
     * [
        'touser' => 'user-openid',
        'template_id' => 'template-id',
        'url' => 'https://easywechat.org',
        'miniprogram' => [
            'appid' => 'xxxxxxx',
            'pagepath' => 'pages/xxx',
        ],
        'data' => [
            'key1' => 'VALUE',
            'key2' => 'VALUE2',
        ...
        ],
     */
    public function sendTemplate($msg)
    {
        return $this->app->template_message->send($msg);
    }
    /**
     * 发送模板消息
     * [
    'touser' => 'user-openid',
    'template_id' => 'template-id',
    'url' => 'https://easywechat.org',
    'scene' => 1000,
    'data' => [
        'key1' => 'VALUE',
        'foo' => '你好',  // 不需要指定颜色
        'bar' => ['你好', '#F00'], // 指定为红色
        'baz' => ['value' => '你好', 'color' => '#550038'], // 与第二种一样
        'zoo' => ['value' => '你好'], // 与第一种一样,
        ...
    ]],
     */
    public function sendSubscription($msg)
    {
        return $this->app->template_message->sendSubscription($msg);
    }

    /**
     * 删除菜单
     */
    public function deleteMenu()
    {
        $this->app->menu->delete();
    }

    /**
     * 通过openid获取公众号用户信息
     */
    public function getUserByQrscene($scene)
    {
        $redis = ApplicationContext::getContainer()->get(Redis::class);
        if($userinfo = $redis->get('qrscene_'.$scene)){
            return Result::data_success(json_decode($userinfo,true));
        }else{
            return Result::data_error('fail');
        }
    }

    /**
     * 微信各终端登录
     * @param $code
     * @param array $userInfo
     * @return array
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
     */
    public function login($code,$userInfo=[])
    {
        $data = [];

        switch ($this->clientType){
            case 'mp':

                $res = $this->app->auth->session($code);
                if(isset($res['errcode'])){
                    return Result::error($res['errmsg'],$res);
                }
                //判断是否绑定了开放平台
                $data['unionid'] = isset($res['unionid'])&&!empty($res['unionid'])?$res['unionid']:'';
                $data['mp_openid'] = $res['openid'];

                $info = json_decode($userInfo['rawData'],true);
                $data['nick_name'] = $info['nickName'];
                $data['avatar'] =  $info['avatarUrl'];

                break;
            case 'h5':
                $userinfo = $this->app->oauth->userFromCode($code)->getRaw();//如果你想拿微信返回给你的原样的全部信息，请使用：$user->getRaw();
                if(!empty($userinfo)){
                    $data['unionid'] = isset($userinfo['unionid'])&&!empty($userinfo['unionid'])?$userinfo['unionid']:'';
                    $data['official_openid'] = $userinfo['openid'];
                    $data['nick_name'] = $userinfo['nickname'];
                    $data['avatar'] = $userinfo['headimgurl'];
                }else{
                    return Result::data_error('微信授权失败');
                }

                break;
            case 'pc':
                break;
            case 'app':
                //原生APP 微信授权登陆
                $appid=$this->config['app_id'];
                $secret= $this->config['secret'];
                $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$secret&code=$code&grant_type=authorization_code";
                $res = file_get_contents($url);
                $authInfo = json_decode($res,true);
                $res = file_get_contents('https://api.weixin.qq.com/sns/userinfo?access_token='.$authInfo['access_token'].'&openid='.$authInfo['openid']);
                if($res){
                    $userinfo = json_decode($res,true);
                    $data['unionid'] = isset($userinfo['unionid'])&&!empty($userinfo['unionid'])?$userinfo['unionid']:'';
                    $data['app_openid'] = $userinfo['openid'];
                    $data['nick_name'] = $userinfo['nickname'];
                    $data['avatar'] = $userinfo['headimgurl'];
                }else if(isset($res['errcode'])){
                    return Result::error($res['errmsg'],$res);
                }else{
                    return Result::error('APP授权失败');
                }
                break;
            case 'work':
                break;
            case 'open':
                $res = $this->app->handleAuthorize($code);
                var_dump($res);
                if($res){
                    $userinfo = $this->app->getAuthorizer($this->config['app_id']);
                    $data['unionid'] = isset($userinfo['unionid'])&&!empty($userinfo['unionid'])?$userinfo['unionid']:'';
                    $data['open_openid'] = $userinfo['openid'];
                    $data['nick_name'] = $userinfo['nickname'];
                    $data['avatar'] = $userinfo['headimgurl'];
                }else if(isset($res['errcode'])){
                    return Result::error($res['errmsg'],$res);
                }else{
                    return Result::error('APP授权失败');
                }
                break;
            default:
                break;
        }
        return Result::data_success($data);
    }

    /**###############################
     * ##########   支付   ###########
     * ##############################
     */
    /**
     * 统一支付
     * @param $orderData
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function unifyPay($orderData,$notifyUrl='')
    {
        //通过后台配置的支付回调进行绑定
        if($notifyUrl==''){
            $orderData['notify_url'] = $this->config['notify_url'];
        }
        $payment = Factory::payment($this->config);
        $res = $payment->order->unify($orderData);
        return $res;
    }

    public function makeSign($data,$signType = 'MD5')
    {
        return for_make_sign($data,$signType,$this->config['key']);
    }
    /**
     * 退款
     * @param string $transactionId
     * @param string $refundNumber
     * @param int $totalFee
     * @param int $refundFee
     * @param array $config
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public function refund(string $transactionId, string $refundNumber, int $totalFee, int $refundFee, array $config = [])
    {
        $payment = Factory::payment($this->config);
        $res = $payment->refund->byTransactionId($transactionId,$refundNumber,$totalFee,$refundFee,$config);
        return $res;
    }

    /**
     * 单个订单查询
     * @param $out_trade_no
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public function queryBill($out_trade_no)
    {
        $payment = Factory::payment($this->config);
        return $payment->order->queryByOutTradeNumber($out_trade_no);
    }
}